#ifndef Rectangle_h
#define Rectangle_h

#include <Defines.h>
#include <CSharpPropertyMethods.h>

// Riccsson includes.
#include <System/Object.h>
#include <System/Generic/IEquatable.h>
#include <System/Type.h>
#include <System/ArgumentException.h>
#include <System/ArgumentNullException.h>
#include <System/Array.h>
#include <System/Activator.h>
#include <System/Int32.h>
#include <System/Math.h>

// XNA includes.
#include "Point.h"

namespace Riccsson
{
	namespace Xna
	{
		namespace Framework
		{
			struct Rectangle
				: public Riccsson::System::Object
				, public Riccsson::System::Generic::IEquatable<Rectangle>
			{
				PROP3(Rectangle, int, Left)
				PROP3(Rectangle, int, Right)
				PROP3(Rectangle, int, Top)
				PROP3(Rectangle, int, Bottom)
				PROP3(Rectangle, Point, Location)
				PROP3(Rectangle, Point, Center)
				PROP3(Rectangle, bool, IsEmpty)


				#pragma region Public Fields

				public: int X;
				public: int Y;
				public: int Width;
				public: int Height;

				#pragma endregion Public Fields


				#pragma region Public Properties

				public: static Rectangle& Empty;

				public: PROP3_GET(int, Left)
				{
					return this->X;
				}
				private: PROP3_SET(int, Left) {throw;}

				public: PROP3_GET(int, Right)
				{
					return (this->X + this->Width);
				}
				private: PROP3_SET(int, Right) {throw;}

				public: PROP3_GET(int, Top)
				{
					return this->Y;
				}
				private: PROP3_SET(int, Top) {throw;}

				public: PROP3_GET(int, Bottom)
				{
					return (this->Y + this->Height);
				}
				private: PROP3_SET(int, Bottom) {throw;}

				#pragma endregion Public Properties


				#pragma region Constructors

				public: Rectangle()
					: PROP3_INIT(Rectangle, Left)
					, PROP3_INIT(Rectangle, Right)
					, PROP3_INIT(Rectangle, Top)
					, PROP3_INIT(Rectangle, Bottom)
					, PROP3_INIT(Rectangle, Location)
					, PROP3_INIT(Rectangle, Center)
					, PROP3_INIT(Rectangle, IsEmpty)
				{
					this->X = 0;
					this->Y = 0;
					this->Width = 0;
					this->Height = 0;
				}

				public: Rectangle(int x, int y, int width, int height)
					: PROP3_INIT(Rectangle, Left)
					, PROP3_INIT(Rectangle, Right)
					, PROP3_INIT(Rectangle, Top)
					, PROP3_INIT(Rectangle, Bottom)
					, PROP3_INIT(Rectangle, Location)
					, PROP3_INIT(Rectangle, Center)
					, PROP3_INIT(Rectangle, IsEmpty)
				{
					this->X = x;
					this->Y = y;
					this->Width = width;
					this->Height = height;
				}

				COPY_CONSTRUCTOR(Rectangle)
				{
					PROP3_INIT_COPY(Rectangle, int, Left);
					PROP3_INIT_COPY(Rectangle, int, Right);
					PROP3_INIT_COPY(Rectangle, int, Top);
					PROP3_INIT_COPY(Rectangle, int, Bottom);
					PROP3_INIT_COPY(Rectangle, Point, Location);
					PROP3_INIT_COPY(Rectangle, Point, Center);
					PROP3_INIT_COPY(Rectangle, bool, IsEmpty);

					this->X = other.X;
					this->Y = other.Y;
					this->Width = other.Width;
					this->Height = other.Height;
				}

				#pragma endregion Constructors


				#pragma region Public Methods

				public: void Offset(Point offset)
				{
					X += offset.X;
					Y += offset.Y;
				}

				public: void Offset(int offsetX, int offsetY)
				{
					X += offsetX;
					Y += offsetY;
				}
		
				public: PROP3_GET(Point, Location)
				{
					return Point(this->X, this->Y);
				}
				public: PROP3_SET(Point, Location)
				{
					X = value.X;
					Y = value.Y;
				}
		
				public: PROP3_GET(Point, Center)
				{
					// This is incorrect
					//return new Point( (this->X + this->Width) / 2,(this->Y + this->Height) / 2 );
					// What we want is the Center of the rectangle from the X and Y Origins
					return Point(this->X + (this->Width / 2), this->Y + (this->Height / 2));
				}
				private: PROP3_SET(Point, Center) { throw; }


						
				public: bool Contains(int x, int y)
				{
					return ((((this->X <= x) && (x < (this->X + this->Width))) && (this->Y <= y)) && (y < (this->Y + this->Height)));
				}
		
				public: bool Contains(Point value)
				{
					return ((((this->X <= value.X) && (value.X < (this->X + this->Width))) && (this->Y <= value.Y)) && (value.Y < (this->Y + this->Height)));
				}

				public: bool Contains(Rectangle value)
				{
					return ((((this->X <= value.X) && ((value.X + value.Width) <= (this->X + this->Width))) && (this->Y <= value.Y)) && ((value.Y + value.Height) <= (this->Y + this->Height)));
				}


				public: void Inflate(int horizontalValue, int verticalValue)
				{
					X -= horizontalValue;
					Y -= verticalValue;
					Width += horizontalValue * 2;
					Height += verticalValue * 2;
				}
		
				public: PROP3_GET(bool, IsEmpty)
				{
					return ((((this->Width == 0) && (this->Height == 0)) && (this->X == 0)) && (this->Y == 0));
				}
				private: PROP3_SET(bool, IsEmpty){throw;}

				public: bool Equals(Rectangle* other)
				{
					return this == other;
				}

				public: override bool Equals(Riccsson::System::object* obj)
				{
					return ( instanceof_const(obj, Rectangle)) ? this == ((Rectangle*)obj) : false;
				}

				public: override GC_PTR<Riccsson::System::string> ToString()
				{
					//return string.Format("{{X:{0} Y:{1} Width:{2} Height:{3}}}", X, Y, Width, Height);
					throw;
				}

				public: override int GetHashCode()
				{
					return (this->X ^ this->Y ^ this->Width ^ this->Height);
				}

				public: bool Intersects(Rectangle& value)
				{
					return value.Left  < Right       && 
							Left       < value.Right && 
							value.Top  < Bottom      &&
							Top        < value.Bottom;            
				}


				public: void Intersects(Rectangle ref value, bool out result)
				{
					result = value.Left	   < Right       && 
								Left       < value.Right && 
								value.Top  < Bottom      &&
								Top        < value.Bottom;
				}

				public: static Rectangle Intersect(Rectangle value1, Rectangle value2)
				{
					Rectangle rectangle;
					Intersect(/*ref*/ value1, /*ref*/ value2, /*out*/ rectangle);
					return rectangle;
				}


				public: static void Intersect(Rectangle ref value1, Rectangle ref value2, Rectangle out result)
				{
					if (value1.Intersects(value2))
					{
						int right_side = Riccsson::System::Math::Min(value1.X + value1.Width, value2.X + value2.Width);
						int left_side = Riccsson::System::Math::Max(value1.X, value2.X);
						int top_side = Riccsson::System::Math::Max(value1.Y, value2.Y);
						int bottom_side = Riccsson::System::Math::Min(value1.Y + value1.Height, value2.Y + value2.Height);
						result = Rectangle(left_side, top_side, right_side - left_side, bottom_side - top_side);
					}
					else
					{
						result = Rectangle(0, 0, 0, 0);
					}
				}
		
				public: static Rectangle Union(Rectangle value1, Rectangle value2)
				{
					int x = Riccsson::System::Math::Min (value1.X, value2.X);
					int y = Riccsson::System::Math::Min (value1.Y, value2.Y);
					return Rectangle(x, y,
											Riccsson::System::Math::Max (value1.Right, value2.Right) - x,
												Riccsson::System::Math::Max (value1.Bottom, value2.Bottom) - y);
				}
		
				public: static void Union(Rectangle ref value1, Rectangle ref value2, Rectangle out result)
				{
					result.X = Riccsson::System::Math::Min (value1.X, value2.X);
					result.Y = Riccsson::System::Math::Min (value1.Y, value2.Y);
					result.Width = Riccsson::System::Math::Max (value1.Right, value2.Right) - result.X;
					result.Height = Riccsson::System::Math::Max (value1.Bottom, value2.Bottom) - result.Y;
				}
				
				#pragma endregion Public Methods
			};

			inline bool operator ==(const Riccsson::Xna::Framework::Rectangle& a, const Riccsson::Xna::Framework::Rectangle& b)
			{
				return ((a.X == b.X) && (a.Y == b.Y) && (a.Width == b.Width) && (a.Height == b.Height));
			}

			inline bool operator !=(const Riccsson::Xna::Framework::Rectangle& a, const Riccsson::Xna::Framework::Rectangle& b)
			{
				return !(((a.X == b.X) && (a.Y == b.Y) && (a.Width == b.Width) && (a.Height == b.Height)));
			}
		}
	}
}

#endif